/*
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
 ** You may obtain a copy of the License at
 **
 **     http://www.apache.org/licenses/LICENSE-2.0
 **
 ** Unless required by applicable law or agreed to in writing, software
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */

package org.geohunter;

import com.google.android.maps.GeoPoint;

import org.geohunter.GeocacheType;
import org.geohunter.GeocacheFactory;
import org.geohunter.GeocacheFactory.Provider;
import org.geohunter.Source;
import org.geohunter.activity.compass.GeoUtils;
import org.geohunter.database.DbFrontend;

import android.graphics.drawable.Drawable;
import android.util.FloatMath;
import java.util.List;

/**
 * Geocache or letterbox description, id, and coordinates.
 */
public class Geocache extends GeoObject {
    public static final String GEOHUNTER_DIR = "/sdcard/GeoHunter";

    public static final String ID = "geocacheId";

    private final GeocacheType mCacheType;
    private final int mContainer;
    /** Difficulty rating * 2 (difficulty=1.5 => mDifficulty=3) */
    private final int mDifficulty;
    private GeoPoint mGeoPoint;
    private final CharSequence mId;
    private final double mLatitude;
    private final double mLongitude;
    private final CharSequence mName;
    private final Source mSource;
    private final int mTerrain;
    
    private Drawable mIcon = null;
    //private Drawable mIconBig = null;
    private Drawable mIconMap = null;

    private List<Integer> mTags = null;
    
    public Geocache(CharSequence id, CharSequence name, double latitude, double longitude,
            Source source, GeocacheType cacheType, int difficulty, int terrain,
            int container) {
        mId = id;
        mName = name;
        mLatitude = latitude;
        mLongitude = longitude;
        mSource = source;
        mCacheType = cacheType;
        mDifficulty = difficulty;
        mTerrain = terrain;
        mContainer = container;
    }

    public List<Integer> getTags(DbFrontend dbFrontend) {
        if (mTags == null)
            mTags = dbFrontend.getGeocacheTags(mId);
        return mTags;
    }
    
    public void flushTags() {
        mTags = null;
    }

    public void updateCachedTag(int tag, boolean set) {
        if (mTags == null)
            return;
        if (set)
            mTags.add(tag);
        else
            mTags.remove(Integer.valueOf(tag));
    }
    
    /*
    public float[] calculateDistanceAndBearing(Location here) {
        if (here != null) {
            Location.distanceBetween(here.getLatitude(), here.getLongitude(), getLatitude(),
                    getLongitude(), mDistanceAndBearing);

            return mDistanceAndBearing;
        }
        mDistanceAndBearing[0] = -1;
        mDistanceAndBearing[1] = -1;
        return mDistanceAndBearing;
    }
    */

    public GeocacheType getCacheType() {
        return mCacheType;
    }

    public int getContainer() {
        return mContainer;
    }
    
    @Override
    public Drawable getIcon() {
        return mIcon;
    }

    public void setIcon(Drawable icon) {
        mIcon = icon;
    }
    
    @Override
    public Drawable getIconMap() {
        return mIconMap;
    }
    
    public void setIconMap(Drawable iconMap) {
        mIconMap = iconMap;
    }
    
    public GeocacheFactory.Provider getContentProvider() {
        // Must use toString() rather than mId.subSequence(0,2).equals("GC"),
        // because editing the text in android produces a SpannableString rather
        // than a String, so the CharSequences won't be equal.
        String prefix = mId.subSequence(0, 2).toString();
        for (Provider provider : GeocacheFactory.ALL_PROVIDERS) {
            if (prefix.equals(provider.getPrefix()))
                return provider;
        }
        return Provider.GROUNDSPEAK;
    }

    public int getDifficulty() {
        return mDifficulty;
    }

    //Formerly calculateDistanceFast
    public float getDistanceTo(double latitude, double longitude) {
        double dLat = Math.toRadians(latitude - mLatitude);
        double dLon = Math.toRadians(longitude - mLongitude);
        final float sinDLat = FloatMath.sin((float)(dLat / 2));
        final float sinDLon = FloatMath.sin((float)(dLon / 2));
        float a = sinDLat * sinDLat + FloatMath.cos((float)Math.toRadians(mLatitude))
                * FloatMath.cos((float)Math.toRadians(latitude)) * sinDLon * sinDLon;
        float c = (float)(2 * Math.atan2(FloatMath.sqrt(a), FloatMath.sqrt(1 - a)));
        return 6371000 * c;
    }
    
    public CharSequence getFormattedAttributes() {
        if (mDifficulty == 0 && mTerrain == 0)
            return "";
        if (mDifficulty == 0)
            return "? / " + (mTerrain / 2.0);
        if (mTerrain == 0)
            return (mDifficulty / 2.0) + " / ?";
        return (mDifficulty / 2.0) + " / " + (mTerrain / 2.0);
    }

    @Override
    public GeoPoint getGeoPoint() {
        if (mGeoPoint == null) {
            int latE6 = (int)(mLatitude * GeoUtils.MILLION);
            int lonE6 = (int)(mLongitude * GeoUtils.MILLION);
            mGeoPoint = new GeoPoint(latE6, lonE6);
        }
        return mGeoPoint;
    }

    public CharSequence getId() {
        return mId;
    }

    @Override
    public double getLatitude() {
        return mLatitude;
    }

    @Override
    public double getLongitude() {
        return mLongitude;
    }

    public CharSequence getName() {
        return mName;
    }

    public Source getSource() {
        return mSource;
    }

    public int getTerrain() {
        return mTerrain;
    }

    /** The icons will be recalculated the next time they are needed. */
    public void flushIcons() {
        mIcon = null;
        mIconMap = null;
    }

    public boolean hasTag(int tag, DbFrontend dbFrontend) {
        List<Integer> tags = getTags(dbFrontend);
        return tags.contains(tag);
    }
}
